package common;

import com.google.common.collect.Maps;
import org.junit.Test;

import java.util.*;
import java.util.stream.*;

public class Example_Lamdba_test {

    class Students {
        private String name;
        private String animals;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAnimals() {
            return animals;
        }

        public void setAnimals(String animals) {
            this.animals = animals;
        }

        public Students(String name, String animals) {
            this.name = name;
            this.animals = animals;
        }

        @Override
        public String toString() {
            return "Students{" +
                    "name='" + name + '\'' +
                    ", animals='" + animals + '\'' +
                    '}';
        }
    }

    class Person {
        String pName;
        String pType;
        int age;

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getpName() {
            return pName;
        }

        public void setpName(String pName) {
            this.pName = pName;
        }

        public String getpType() {
            return pType;
        }

        public void setpType(String pType) {
            this.pType = pType;
        }

        public Person(String pName, String pType) {
            this.pName = pName;
            this.pType = pType;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "pName='" + pName + '\'' +
                    ", pType='" + pType + '\'' +
                    '}';
        }
    }

    /**
     * Lamdba的 Collectors.groupingBy 功能很强大，实现了类似sql的分组函数，它的方法如下：
     * Collectors.groupingBy((Function<? super T, ? extends K> classifier,
     * Supplier<M> mapFactory,
     * Collector<? super T, A, D> downstream)
     * <p>
     * 参数说明：
     * classifier:按照什么分类
     * mapFactory:返回值类型   默认的返回值是HashMap:new
     * downstream:分组的值的返回类型，默认是Collectors.toList()
     */
    @Test
    public void groupAndOrderBy() {
        List<Students> list = getStudents();

        TreeMap<String, Set<String>> collect = list.stream().
                collect(Collectors.groupingBy(Students::getAnimals, TreeMap::new,
                        Collectors.mapping(Students::getName, Collectors.toSet())));

        System.out.println(collect);

    }

    private List<Students> getStudents() {
        List<Students> list = new ArrayList<>();
        list.add(new Students("张三", "猴"));
        list.add(new Students("李四", "鸡"));
        list.add(new Students("王五", "狗"));
        list.add(new Students("杨宏堂", "猴"));
        list.add(new Students("杨宏亮", "狗"));
        list.add(new Students("郭明强", "猴"));
        return list;
    }


    private List<Person> getPersons() {
        List<Person> list = new ArrayList<>();
        list.add(new Person("张三", "猴"));
        list.add(new Person("李四", "鸡"));
        list.add(new Person("王五", "狗"));
        list.add(new Person("杨宏堂", "猴"));
        list.add(new Person("杨宏亮", "狗"));
        list.add(new Person("郭明强", "猴"));
        return list;
    }


    /**
     * 差集
     */
    @Test
    public void test1() {
        List<Students> studentsList = getStudents();
        List<Students> studentsList1 = getStudents();
        //studentList-studentsList1  1 2  3  4     1 2-----------》   3  4
        List<Students> collect = studentsList.stream().filter(e -> !studentsList1.contains(e)).collect(Collectors.toList());
    }

    /**
     * 优雅的将一个对象的集合转化成另一个对象的集合
     */
    @Test
    public void test2() {
        List<Students> studentsList = getStudents();
        List<Person> personList = studentsList.stream().map(e -> new Person(e.getName(), e.getAnimals()))
                .collect(Collectors.toList());
        System.out.println(personList);
    }

    /**
     * 并集
     */
    @Test
    public void test3() {
        List<Students> studentsList = getStudents();
        List<Person> personList = getPersons();
        List<String> name1 = studentsList.parallelStream().map(e -> e.getName()).collect(Collectors.toList());
        List<String> name2 = personList.parallelStream().map(e -> e.getpName()).collect(Collectors.toList());
        name1.addAll(name2);
    }

    /**
     * 去重
     */
    @Test
    public void test4() {
        List<Students> studentsList = getStudents();
        List<String> name1 = studentsList.parallelStream().distinct().map(e -> e.getName()).collect(Collectors.toList());
    }

    /**
     * Stream.of（）创建顺序流
     */
    public void test55() {
        IntStream intStream = IntStream.of(12, 14, 16);
        intStream.forEach(e -> System.out.println(e));

        System.out.println("--- LongStream ---");
        LongStream longStream = LongStream.of(154L, 236L, 306L);
        longStream.forEach(e -> System.out.println(e));

        System.out.println("--- DoubleStream ---");
        DoubleStream doubleStream = DoubleStream.of(123.56, 456.87, 784.65);
        doubleStream.forEach(e -> System.out.println(e));
    }

    /**
     * 将一个Map转化成List，并对List中的对象按照原来Map的key进行排序
     */
    @Test
    public void test5() {
        Map<String, String> map = getListPerson();
        //按照Key排序
        List<Person> list = map.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey()))
                .map(e -> new Person(e.getKey(), e.getValue())).collect(Collectors.toList());
        list.stream().forEach(System.out::print);
        System.out.println();
        //按照Value排序排序
        List<Person> list1 = map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getValue))
                .map(e -> new Person(e.getKey(), e.getValue())).collect(Collectors.toList());
        list1.stream().forEach(System.out::print);
        System.out.println();
        //按照Key排序排序
        List<Person> list2 = map.entrySet().stream().sorted(Map.Entry.comparingByKey())
                .map(e -> new Person(e.getKey(), e.getValue())).collect(Collectors.toList());
        list2.stream().forEach(System.out::print);
        System.out.println();
    }

    private Map<String, String> getListPerson() {
        Map<String, String> map = Maps.newLinkedHashMap();
        map.put("bbb", "yht1");
        map.put("ccc", "yht1");
        map.put("aaa", "yht");
        map.put("ddd", "yht");
        return map;
    }

    /**
     * List转化成Map
     */
    @Test
    public void test6() {
        /*使用Collectors.toMap形式*/
        Map<String, String> result = getStudents().stream().collect(Collectors.toMap(p -> p.name, p -> p.animals, (k1, k2) -> k1));
//其中Collectors.toMap方法的第三个参数为键值重复处理策略，如果不传入第三个参数，当有相同的键时，会抛出一个IlleageStateException。
//或者
        Map<String, String> result1 = getStudents().stream().collect(Collectors.toMap(Students::getName, Students::getAnimals));

        Map<String, String> result2 = getStudents().stream().collect(HashMap::new, (map, p) -> map.put(p.name, p.getAnimals()), Map::putAll);


    }

    /**
     * Lamdba表达式避免stream流空指针异常
     */
    public void test7() {
        List<Students> students = getStudents();
        //value为null情况
        Map<String, String> collect = students.stream()
                .collect(Collectors.toMap(p -> p.getName(), p -> Optional.ofNullable(p.getAnimals()).orElse(""), (k1, k2) -> k2));
        System.out.println(collect);
        //键不为null   同时key重复时候，使用新的值
        Map<String, String> collect1 = students.stream()
                .filter(p -> p.getName() != null).collect(Collectors.toMap(p -> p.getName(), p -> p.getAnimals(), (k1, k2) -> k2));
        System.out.println(collect1);

        Map<String, String> memberMap = students.stream().collect(HashMap::new, (m, v) ->
                m.put(v.getName(), v.getAnimals()), HashMap::putAll);
        System.out.println(memberMap);


    }

    /**
     * String.join 和   Collectors.joining 用法
     */
    @Test
    public void test8() {
        //java8 String.join 方式
        List<String> webs = Arrays.asList("voidcc.com", "voidmvn.com", "voidtool.com");
        //webs 必须是List<String>
        String allwebs = String.join(",", webs);
        System.out.println(allwebs);


        //    Collectors joining
        //     Collectors.joining 收集Stream中的值，该方法可以方便地将Stream得到一个字符串。joining函数接受三个参数，分别表示允（用以分隔元素）、前缀和后缀:
        //

        String allwebs2 = webs.stream().collect(Collectors.joining(","));
        System.out.println(allwebs2);
        String strJoin = Stream.of("1", "2", "3", "4")
                .collect(Collectors.joining(",", "[", "]"));
        System.out.println("strJoin: " + strJoin);


    }

    /**
     * Collectors toSet
     */
    @Test
    public void test9() {
        Set<String> result = Stream.of("aa", "bb", "cc", "aa").collect(HashSet::new, HashSet::add, HashSet::addAll);
//Collectors类中已经预定义好了toList，toSet，toMap，toCollection等方便使用的方法，所以以上代码还可以简化如下：
        Set<String> result2 = Stream.of("aa", "bb", "cc", "aa").collect(Collectors.toSet());

        Set<Integer> collectSet = Stream.of(1, 2, 3, 4).collect(Collectors.toSet());
        System.out.println("collectSet: " + collectSet);
// 打印结果 collectSet: [1, 2, 3, 4]

        Stack stack1 = getPersons().stream().collect(Collectors.toCollection(Stack::new));


    }

    /**
     * 排序
     */
    @Test
    public void test11() {
        //按照自然顺序进行排序 如果要自定义排序sorted 传入自定义的 Comparator
        getPersons().stream()
                .sorted()
                .filter((s) -> s.getpName().startsWith("a"))
                .forEach(System.out::println);

//对象排序比较 请重写对象的equals()和hashCode()方法
        List<Person> collect = getPersons().stream().sorted((a, b) -> b.getpName().compareTo(a.getpName())).collect(Collectors.toList());

        Collections.sort(getPersons(), (a, b) -> b.getpName().compareTo(a.getpName()));

    }

    /*
      比较
     */
    @Test
    public void test12() {
        Comparator<Person> comparator = Comparator.comparing(Person::getpName).thenComparing(Person::getpType);

        Person p1 = new Person("John", "Doe");
        Person p2 = new Person("Alice", "Wonderland");

        comparator.compare(p1, p2);             // > 0
        comparator.reversed().compare(p1, p2);  // < 0

    }

    /**
     * grouping by 分组
     */
    @Test
    public void test13() {
        List<Person> persons = getPersons();
        Map<String, Long> collect = persons.stream().collect(Collectors.groupingBy(Person::getpName, Collectors.counting()));

        Map<Boolean, List<Integer>> collectGroup = Stream.of(1, 2, 3, 4)
                .collect(Collectors.groupingBy(it -> it > 3));
        System.out.println("collectGroup : " + collectGroup);
// 打印结果
// collectGroup : {false=[1, 2, 3], true=[4]}

    }
    /**
     * partationingBY
     * Collectors中还存在一个类似groupingBy的方法：partitioningBy，它们的区别是partitioningBy为键值为Boolean类型的groupingBy，
     * 这种情况下它比groupingBy更有效率。
     * partitioningBy 将数字的Stream分解成奇数集合和偶数集合。
     */
    @Test
    public void test14(){
        Map<Boolean, List<Integer>> collectParti = Stream.of(1, 2, 3, 4)
                .collect(Collectors.partitioningBy(it -> it % 2 == 0));
        System.out.println("collectParti : " + collectParti);
// 打印结果
// collectParti : {false=[1, 3], true=[2, 4]}

    }

    /**
     * Collectors分别提供了求平均值averaging、总数couting、最小值minBy、最大值maxBy、求和suming等操作。
     * 但是假如你希望将流中结果聚合为一个总和、平均值、最大值、最小值，那么Collectors.summarizing(Int/Long/Double)就是为你准备的，
     * 它可以一次行获取前面的所有结果，其返回值为(Int/Long/Double)SummaryStatistics。
     */
    @Test
    public void test15(){
        List<Person> persons = getPersons();
        IntSummaryStatistics dss = persons.stream().collect(Collectors.summarizingInt((Person p) -> p.getAge()));
        double average=dss.getAverage();
        double max=dss.getMax();
        double min=dss.getMin();
        double sum=dss.getSum();
        double count=dss.getCount();

        IntSummaryStatistics ageSummary = persons
                .stream()
                .collect(Collectors.summarizingInt(p -> p.age));

        System.out.println(ageSummary);
// IntSummaryStatistics{count=4, sum=76, min=12, average=19.000000, max=23}


    }
}
